{\rtf0\ansi{\fonttbl\f0\fmodern Courier;\f1\ftech Symbol;\f3\fmodern Ohlfs;}
\margl40
\margr40
{\colortbl;\red0\green0\blue0;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i0\ulnone\fs24\fc0\cf0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//
\i 	
\b FILENAME
\b0 :	
\b\i0 eTApp.m\

\b0 //
\i 	
\b SUMMARY
\b0 :	
\b\i0 Implementation of the eTApp application controller
\b0 \
//	
\b\i SUPERCLASS
\b0 :
\i0 	
\b Object:eTApp
\b0 \
//	
\b\i PROTOCOLS
\b0 :
\i0 	
\b None
\b0 \
//	
\b\i INTERFACE
\b0 :
\i0 	
\b None
\b0 \
//	
\b\i AUTHOR
\b0 :		
\b\i0 Rohit Khare
\b0 \
//	
\b\i COPYRIGHT
\b0 :	
\f1\i0 с
\f0\b 1993,94 California Institure of Technology, eText Project\

\b0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i Implementation Comments
\b0\i0 \
//		At initialization time, eTApp allocates and inits the various objects\
//	in the core of eText. It then loads in various custom classes, defined as\
//		1) all *.
\b bundle
\b0  in the appWrapper\
//		2) all *.
\b annotation
\b0  and *.
\b agent
\b0 \
//	in the appWrapper, ~Library/eText, and /LocalLibrary/eText\
//	At that time, all principal classes are -
\b awake
\b0 nd, and 
\b register
\b0  themselves\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i History
\b0\i0 \
//	10/31/94:	
\b Moved external bundles in to core image; see -init Phase III.
\b0 \
//	09/21/94:	
\b Revamped for eText5; cleanup.
\b0 \
//	01/11/94:	
\b Revamped for eText4.
\b0 \
//	09/09/93:	
\b Added - uniqueID;\

\b0 //	08/22/93:
\b 	Moved "Accessor Methods" to eTDoc
\b0 \
//	08/22/93:
\b 	Added - loadToolFromPath; Added "Accessor Methods"
\b0 \
//	08/17/93:
\b 	Implementation of 1) and 2) using FileHandler (in the aW ONLY!)
\b0 \
//	08/16/93:
\b 	Created. Assumes the definition of a <Tool> protocol
\b0 \
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Imported Interfaces
\b0 \
//\
	#import "
\b eTApp.h
\b0 "\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
// 	
\b Global Object Handles
\b0 \
//\
	
\b id
\b0 	/*	eTApp		*/		
\b etApp
\b0 ;\
	
\b id
\b0 	/*	eTAppUI		*/		
\b etAppUI
\b0 ;\
	
\b id
\b0 	/*	Inspector	*/		
\b inspector
\b0 ;\
	
\b id
\b0 	/*	UserModel	*/		
\b userModel
\b0 ;\
	
\b id
\b0 	/*	Navigator	*/		
\b navigator
\b0 ;\
\

\i @implementation eTApp
\i0 \
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
// 	
\b Class Management
\b0 \
//\

\b - init 
\b0 \{	\
	
\b NXAtom
\b0  
\b supportedTypes
\b0 [13]=\{NX
\b Filename
\b0 PboardType,\
								NX
\b FileContents
\b0 PboardType, \
								NX
\b Sound
\b0 PboardType,\
								NX
\b PostScript
\b0 PboardType, \
								NX
\b TIFF
\b0 PboardType,\
								N3D
\b RIB
\b0 PboardType, \
								NX
\b Color
\b0 PboardType,\
								NX
\b Font
\b0 PboardType,\
								NX
\b Ruler
\b0 PboardType,\
								NX
\b RTF
\b0 PboardType,\
								NX
\b Ascii
\b0 PboardType,\
								NX
\b TabularText
\b0 PboardType,\
								NULL\};\
	
\b NXAtom
\b0 						
\b docdir
\b0 ;\
	
\b char
\b0 						
\b thePath
\b0 [MAXPATHLEN];\
\
// 	
\b PHASE I: Internal intialization\

\b0 	[super 
\b init
\b0 ];\
	
\b docTable
\b0  = [[HashTable alloc] 
\b initKeyDesc
\b0 :"i"];\
	
\b agentTable
\b0  = [[HashTable alloc] 
\b initKeyDesc
\b0 :"%"];\
	
\b annotationTable
\b0  = [[HashTable alloc] 
\b initKeyDesc
\b0 :"%"];\
	
\b typesTable
\b0  = [[HashTable alloc] 
\b initKeyDesc
\b0 :"%"];\
	[[NXApp 
\b printInfo
\b0 ] 
\b setHorizPagination
\b0 :NX_FITPAGINATION];\
	[[NXApp 
\b printInfo
\b0 ] 
\b setMarginLeft
\b0 :36.0 right:36.0 top:36.0 bottom:36.0];\
	\
//	
\b PHASE II: Global initialization\

\b0 	
\b etApp
\b0  = self;\
	
\b etAppUI
\b0  = [NXApp delegate];	// 
\i relies on Nib setting
\i0 .\
	
\b inspector
\b0  = [[Inspector alloc] init];\
	
\b userModel
\b0  = [[UserModel alloc] init];\
	
\b navigator
\b0  = [[Navigator alloc] init];\
	[NXApp 
\b registerServicesMenuSendTypes
\b0 :supportedTypes \
						  
\b andReturnTypes
\b0 :supportedTypes];\
\

\fc1\cf1 // 	
\b PHASE III: Locate and load tools and document info\

\b0 	// 
\b First, activate the classes compiled into the Kernel
\b0 \
	\{\
		const char *
\b internalTools
\b0 [12] = \{"
\b eTImage
\b0 ", "
\b eTAudio
\b0 ", "
\b eTLink
\b0 ",\
			"
\b eTBookmark
\b0 ", "
\b Divider
\b0 ", "
\b eTFileLink
\b0 ", "
\b eTLiteral
\b0 ", "
\b eTNote
\b0 ",\
			"
\b eTURLink
\b0 ", "
\b HotLinks
\b0 ", "
\b MailTo
\b0 ", NULL\};\
		int i;\
		\
		for(i=0; 
\b internalTools
\b0 [i]; i++)\
			[
\b objc_lookUpClass
\b0 (
\b internalTools
\b0 [i]) 
\b toolAwake
\b0 :self];\
	\}\
	\
	// 
\b Create a Library directory, public_html and set the defaults
\b0 .\
\
	sprintf(thePath, "
\b %s/Library/eText/
\b0 ", 
\b NXHomeDirectory
\b0 ());\
	
\b if
\b0  (
\b access
\b0 (
\b\fc0\cf0 thePath
\b0\fc1\cf1 ,R_OK|W_OK)) \

\fc0\cf0 		if (
\b mkdir
\b0 (
\b thePath
\b0 , 0777)) \{\
			NXLogError("
\b Could not mkdir(
\b0 \\"%s\\"
\b )
\b0 ", 
\b thePath
\b0 );
\fc1\cf1 \
			perror("eText5");\
		\}\
	[userModel 
\b defaultValue
\b0 :
\b thePath
\b0  
\b for
\b0 :
\b ETFDIRECTORY
\b0 ];\
\
	sprintf(thePath, "
\b %s/public_html/
\b0 ", 
\b NXHomeDirectory
\b0 ());\
	
\b if
\b0  (
\b access
\b0 (
\b\fc0\cf0 thePath
\b0\fc1\cf1 ,R_OK|W_OK)) \

\fc0\cf0 		if (
\b mkdir
\b0 (
\b thePath
\b0 , 0777)) \{\
			NXLogError("
\b Could not mkdir(
\b0 \\"%s\\"
\b )
\b0 ", 
\b thePath
\b0 );
\fc1\cf1 \
			perror("eText5");\
		\}\
	[userModel 
\b defaultValue
\b0 :
\b thePath
\b0  
\b for
\b0 :
\b HTMDIRECTORY
\b0 ];\
\
	// 
\b Now go out and search for the rest
\b0 .\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b bundle$
\b0 " 
\b inDir
\b0 :(STR)[[NXBundle 
\b mainBundle
\b0 ] directory]\
				target:self action:@selector(loadToolFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b annotation$
\b0 " 
\b inLibrary
\b0 :"
\b eText
\b0 "\
				target:self action:@selector(loadToolFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b agent$
\b0 " 
\b inLibrary
\b0 :"
\b eText
\b0 "\
				target:self action:@selector(loadToolFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b accessory
\b0 " 
\b inLibrary
\b0 :"
\b eText
\b0 "\
				target:self action:@selector(loadToolFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b etfd$
\b0 " 
\b inDir
\b0 :[[NXBundle 
\b mainBundle
\b0 ] directory]\
				target:self action:@selector(loadDocInfoFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b etfd$
\b0 " 
\b inDir
\b0 :(STR)
\b NXHomeDirectory
\b0 ()\
				target:self action:@selector(loadDocInfoFromPath:)];\
	[
\b navigator
\b0  
\b find
\b0 :"\\\\.
\b etfd$
\b0 " 
\b inLibrary
\b0 :"
\b eText
\b0 "\
				target:self action:@selector(loadDocInfoFromPath:)];\
	
\b docdir
\b0  = 
\fc0\cf0 [userModel 
\b stringQuery
\b0 :
\b ETFDIRECTORY
\b0 ]
\fc1\cf1 ;\
	if (docdir && *docdir) \{\
		[
\b navigator
\b0  	
\b find
\b0 :"\\\\.
\b etfd$
\b0 " 
\b inDir
\b0 :
\b docdir\

\b0 					target:self action:@selector(loadDocInfoFromPath:)];\
	\}\
	return self;\

\fc0\cf0 \}\
- 
\b free
\b0  \{\
	typesTable = [
\b typesTable 
\b0 free];\
	annotationTable = [
\b annotationTable
\b0  free];\
	agentTable = [
\b agentTable
\b0  free];\
	docTable = [
\b docTable
\b0  free];\
	return self = [
\b super
\b0  free];\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Registration & Lookup
\b0 \
//\
- 
\b registerType
\b0 :(NXAtom) 
\b pbType\
			for
\b0 :(id)
\b factoryClass
\b0  \{\
	
\b if
\b0  (
\b !
\b0 [typesTable 
\b isKey:pbType
\b0 ]) \{\
		[typesTable 
\b insertKey
\b0 :
\b pbType
\b0  
\b value
\b0 :
\b factoryClass
\b0 ];\
		
\b typesList
\b0 [([typesTable count] % 
\fc1\cf1 MAX_PBOARD_TYPES
\fc0\cf0 ) - 1] = 
\b pbType
\b0 ; \
	\}\
	return self;\
\}\
- 
\b registerAgent
\b0 :(id) 
\b factoryClass
\b0  \

\b 			name
\b0 :(NXAtom) 
\b name
\b0 \

\b 	  menuLabel
\b0 :(NXAtom) 
\b label
\b0 \

\b 		menuKey
\b0 :(char) 
\b key
\b0  \{\
	if (![agentTable 
\b isKey:name
\b0 ]) \{\
		[agentTable 
\b insertKey:name
\b0  
\b value:factoryClass
\b0 ];\
		[
\b etAppUI registerAgent
\b0 :label key:key name:name];\
	\}\
	return self;\
\}\
- 
\b registerAnnotation
\b0 :(id) 
\b factoryClass
\b0  \
				 
\b name
\b0 :(NXAtom) 
\b name
\b0 \
		 
\b RTFDirective
\b0 :(NXAtom) 
\b directive
\b0 \
			
\b menuLabel
\b0 :(NXAtom) 
\b label
\b0 \
			  
\b menuKey
\b0 :(char) 
\b key
\b0 \
			 
\b menuIcon
\b0 :(NXImage*) 
\b icon
\b0  \{\
	if (![annotationTable 
\b isKey:name
\b0 ])\{\
		[annotationTable 
\b insertKey:name value:factoryClass
\b0 ];\

\b 		[eText registerDirective:directive forClass:factoryClass];\

\b0 		if ((label && *label))\
			[
\b etAppUI registerAnnotation
\b0 :
\b label
\b0  key:
\b key
\b0  name:
\b name
\b0  icon:
\b icon
\b0 ];\
	\}\
	return self;\
\}\
- 
\b registerAccessory
\b0 :(NXAtom) 
\b menuLabel\

\b0 				 
\b key
\b0 :(char) 
\b key
\b0 \
				
\b name
\b0 :(NXAtom)
\b  name
\b0 \
			  
\b target
\b0 :(id) 
\b theTarget
\b0 \
			  
\b action
\b0 :(SEL) 
\b theAction
\b0  \{\
	return [
\b etAppUI registerAccessory
\b0 :
\b menuLabel
\b0  key:
\b key
\b0  name:
\b name
\b0  target:
\b theTarget
\b0  action:
\b theAction
\b0 ];\
\}\
- 
\b annotationByName
\b0 :(NXAtom) 
\b name
\b0  \{return [
\b annotationTable valueForKey:name
\b0 ];\}\
- 
\b agentByName
\b0 :(NXAtom) 
\b name
\b0  \{return [
\b agentTable valueForKey:name
\b0 ];\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Document Management (by docID)
\b0 \
//\
- 
\b openID
\b0 :(long) 
\b docID
\b0  \{\
	id	
\b theDoc
\b0 ;\
	id	
\b theDocInfo
\b0 ;\
	\
	if ([
\b docTable
\b0  
\b isKey
\b0 :(void *)
\b docID
\b0 ]) \{\
		[(id) [docTable 
\b valueForKey
\b0 :(void *)docID] 
\b makeVisible
\b0 ];\
		return 
\b nil
\b0 ;\
	\}\
	
\b theDocInfo
\b0  = [eTDocInfo 
\b findDocInfo
\b0 :
\b docID
\b0 ];\
	if (!
\b theDocInfo
\b0 ) \{\
		
\b theDocInfo
\b0  = [[
\b eTDocInfo
\b0  alloc] 
\b init
\b0 ];\
		docID = [
\b theDocInfo
\b0  
\b docID
\b0 ];\
	\} 
\b else
\b0  if (![theDocInfo 
\b isVirgin
\b0 ] && \
				
\b access
\b0 ([theDocInfo 
\b docPath
\b0 ], R_OK|X_OK)) \{\
		int choice=NXRunAlertPanel("
\b Invalid docPath
\b0 ",\
					"
\b Did not find %s at the path %s.
\b0 ",\
					"
\b Create New Document
\b0 ", "
\b Cancel
\b0 ", NULL,\
					[theDocInfo 
\b docTitle
\b0 ], [theDocInfo 
\b docPath
\b0 ]);\
		if (choice == NX_ALERTALTERNATE) return 
\b nil
\b0 ;\
	\}\
	
\b theDoc
\b0  = [[
\b eTDoc
\b0  alloc] 
\b initForDocInfo
\b0 :
\b theDocInfo
\b0 ];\
	if (
\b !theDoc
\b0 ) return 
\b nil
\b0 ;\
	[
\b docTable
\b0  
\b insertKey
\b0 :(void *)
\b docID
\b0  value:theDoc];\
	return
\b  theDoc
\b0 ;\
\}\
- 
\b closeID
\b0 : (long) 
\b docID
\b0  \{\
	id 
\b theDoc
\b0 ;\
	\
	if (
\b !
\b0 [
\b docTable
\b0  
\b isKey
\b0 :(void *)
\b docID
\b0 ])\
		return 
\b nil
\b0 ;\
	
\b theDoc
\b0  = [
\b docTable
\b0  
\b valueForKey
\b0 :(void *)
\b docID
\b0 ];\
	[
\b theDoc free
\b0 ];\
	[[
\b eTDocInfo
\b0  
\b findDocInfo
\b0 :docID] 
\b free
\b0 ];\
	[
\b docTable removeKey
\b0 :(void *)docID];\
	return self;\
\}\
- (long) 
\b createID
\b0  \{\
	char		
\b newPath
\b0 [MAXPATHLEN];\
	eTDocInfo	*
\b theDocInfo
\b0 ;\
	static int	
\b count
\b0 =0;\
	const char *
\b docdir
\b0 =[
\b userModel
\b0  stringQuery:
\b ETFDIRECTORY
\b0 ];\
\
	
\b theDocInfo
\b0  = [[
\b eTDocInfo
\b0  alloc] 
\b init
\b0 ];\
	do \{ sprintf(
\b newPath
\b0 , "
\b %s/Untitled-%d.%s
\b0 ", \
			(docdir && *docdir) ? 
\b docdir
\b0  : 
\b NXHomeDirectory
\b0 (), \

\b 			count++
\b0 , 
\b ETFD_EXT
\b0 );\
	\} while (
\b !access
\b0 (newPath, 
\b F_OK
\b0 ));\
	[
\b theDocInfo
\b0  
\b setDocPath
\b0 : NXUniqueString(
\b newPath
\b0 )];\
	[self 
\b openID
\b0 :[
\b theDocInfo
\b0  docID]];\
	return [
\b theDocInfo
\b0  docID];\
\}\
- 
\b documentByID
\b0 : (long) 
\b docID 
\b0 \{return [
\b docTable
\b0  
\b valueForKey
\b0 :(void *)
\b docID
\b0 ];\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Global Operations
\b0 \
//\
- 
\b saveAll
\b0  \{\
	long	key; \
	id		doc; \
	NXHashState  state = [docTable initState];\
	\
	while ([docTable nextState: &state key:(void **)&key value:(void **)&doc]) \
		[
\b doc
\b0  
\b save
\b0 :self];\
	return self;\
\}\
- 
\b shutdown
\b0 :(BOOL) 
\b cancellable
\b0  \{\
	BOOL	
\b dirtyDocs
\b0 ;\
	long	key; \
	id		
\b theDoc
\b0 ; \
	int		choice;\
	NXHashState  state = [docTable initState];\
	\

\b 	recheck:	
\b0 // 
\i label. Cycle until all docs handled
\i0 .\
	dirtyDocs = NO;\
	state = [docTable initState];\
	
\b while
\b0  (
\b !dirtyDocs
\b0  && \
		[docTable 
\b nextState
\b0 : &state key:(void **)&key value:(void **)&theDoc]) \
		
\b dirtyDocs |=
\b0  [theDoc 
\b needsSaving
\b0 ];\
		\
	if (
\b dirtyDocs
\b0 ) \{\
		if (
\b cancellable
\b0 )\
			choice=NXRunAlertPanel("
\b Quit
\b0 ",\
					"There are unsaved files",\
					"Review Unsaved", "Quit Anyway", "
\b Cancel
\b0 ");\
		else choice=NXRunAlertPanel("
\b Logout/Power-Off
\b0 ",\
					"There are unsaved files",\
					"Review Unsaved", "Quit Anyway", 
\b NULL
\b0 );	\
		if (choice == NX_ALERTDEFAULT)\{\
			state = [docTable 
\b initState
\b0 ];\
			
\b while
\b0 ([docTable 
\b nextState
\b0 :&state \
					key:(void **)&key value:(void **)&
\b theDoc
\b0 ]) \
				[
\b theDoc
\b0  
\b close
\b0 :self 
\b allowCancel
\b0 :
\b cancellable
\b0 ];\
		\} else if (choice == NX_ALERTOTHER)\
			return 
\b nil
\b0 ;\
		else if (choice == NX_ALERTALTERNATE)\
			return 
\b self
\b0 ;\
			\
		goto 
\b recheck
\b0 ;\
	\}\
	return 
\b self
\b0 ;	// 
\i this is a significant return value
\i0 .\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Private Methods (for eTAppUI only)
\b0 \
//\
- (NXAtom*)
\b typesList
\b0  \{return 
\b typesList
\b0 ;\}\
- 
\b annotationByPboard
\b0 :
\b thePboard
\b0  \{\
	NXAtom	
\b theType
\b0 ;\
	id ret;\
	\
	
\b theType
\b0 =[thePboard 
\b findAvailableTypeFrom:typesList
\b0  num:[typesTable count]];\
	if (
\b !theType
\b0 ) \{\
		// Filter out filename pboards -- we can't do squat about fcontents\
		if ([thePboard 
\b findAvailableTypeFrom
\b0 :&NX
\b Filename
\b0 PboardType num:1]) \{\
			char	*path,*tmp;\
			int		len;\
\
			[
\b thePboard
\b0  
\b readType
\b0 :NX
\b Filename
\b0 PboardType data:&
\b path
\b0  length:&len];\
			if (index(path, '\\t')) *index(path, '\\t')=0;\
			if ((tmp = rindex(path, '.')) && *(tmp+1))\
				
\b theType
\b0  = NX
\b CreateFileContents
\b0 PboardType(tmp + 1);\
			[thePboard deallocatePasteboardData:path length:len];\

\b\fs36 		\} else \{\
			return nil;\
		\}\

\b0\fs24 	\}\
	
\b ret
\b0  = [typesTable 
\b valueForKey:theType
\b0 ];\
	return (ret ? ret : [typesTable 
\b valueForKey
\b0 :\
						NXCreateFileContentsPboardType(
\b ANY_TYPE
\b0 )]);\
\}\
- (BOOL) 
\b validateSaveAll
\b0  \{\
	BOOL	
\b dirtyDocs
\b0 ;\
	NXAtom	key; \
	id		theDoc; \
	NXHashState  state = [docTable initState];\
	\
	
\b dirtyDocs
\b0  = NO;\
	state = [docTable initState];\
	
\b while
\b0  (!
\b dirtyDocs
\b0  && \
		[docTable 
\b nextState
\b0 : &state key:(void **)&key value:(void **)&theDoc]) \
		
\b dirtyDocs |=
\b0  [theDoc 
\b needsSaving
\b0 ];\
	return dirtyDocs;\
\}\
- 
\b loadToolFromPath
\b0 :(const char*)
\b path
\b0  \{\
	id theBundle = [[
\b NXBundle
\b0  alloc] 
\b initForDirectory
\b0 :NXUniqueString(
\b path
\b0 )];\
	if ([[theBundle principalClass] conformsTo:
\b @protocol(Tool
\b0 )])\
		[[theBundle 
\b principalClass
\b0 ] 
\b toolAwake
\b0 :self];\
	return self;\
\}\
- 
\b loadDocInfoFromPath
\b0 :(const char*)
\b path
\b0  \{\
	[[[
\b eTDocInfo
\b0  alloc] 
\b init
\b0 ] 
\b readComponentFromPath
\b0 :
\b path
\b0 ];\
	return self;\
\}\
- 
\b docTable
\b0  \{\
	return 
\b docTable
\b0 ;\}\
@end
}
